#pragma once
#include "online.hpp"

#define BOARD_ROW 15
#define BOARD_COL 15
#define CHESS_WHITE 1
#define CHESS_BLACK 2

enum class room_status
{
    GAME_START,
    GAME_OVER
};

class room
{
public:
    room(int roomid,user_table* table,online_manager* online)
    :_roomid(roomid)
    ,_status(room_status::GAME_START)
    ,_playercount(0)
    ,_table(table)
    ,_online(online)
    ,_board(BOARD_ROW,std::vector<int>(BOARD_COL,0))
    {
        DLOG("%d房间创建成功!",_roomid);
    }

    ~room()
    {
        DLOG("%d房间销毁成功!",_roomid);
    }

    void handle_request(const Json::Value& req)
    {
        Json::Value resp;
        int roomid = req["room_id"].asInt();
        if(_roomid != roomid)
        {
            resp["result"] = false;
            resp["reason"] = "房间号不匹配";
            resp["optype"] = req["optype"].asString();
            broadcast(resp);
            return;
        }
        std::string op = req["optype"].asString();
        if(op == "put_chess")
        {
            resp = handle_chess(req);
            if(resp["is_win"].asBool())
            {
                int winner = resp["uid"].asInt();
                int loser = (winner == _whiteid ? _blackid : _whiteid);
                _table->win(winner);
                _table->lose(loser);
                _status = room_status::GAME_OVER;
            }
        }
        else if(op == "chat")
        {
            resp = handle_chat(req);
        }
        broadcast(resp);
    }

    void broadcast(const Json::Value& resp)
    {
        std::string body;
        json_util::serialize(resp,body);
        wsserver_t::connection_ptr wconn  = _online->get_conn_from_room(_whiteid);
        if(wconn.get() != nullptr)
        {
            wconn->send(body);
        }
        else
        {
            DLOG("%d房间白棋玩家链接获取失败!",_roomid);
        }
        wsserver_t::connection_ptr bconn  = _online->get_conn_from_room(_blackid);
        if(bconn.get() != nullptr)
        {
            bconn->send(body);
        }
        else
        {
            DLOG("%d房间黑棋玩家链接获取失败!",_roomid);
        }
    }

    Json::Value handle_chat(const Json::Value& req)
    {
        Json::Value resp = req;
        resp["result"] = true;
        return resp;
    }

    Json::Value handle_chess(const Json::Value& req)
    {
        Json::Value resp = req;
        int row =  req["row"].asInt();
        int col = req["col"].asInt();
        if(_board[row][col]  != 0)
        {
            resp["result"] = false;
            resp["reason"]= "位置已占用";
            return resp;
        }
        int uid =  req["uid"].asInt();
        int userchess = (uid == _whiteid ? CHESS_WHITE : CHESS_BLACK);
        _board[row][col] = userchess;
        bool ret = is_win(row,col,userchess);
        resp["is_win"] = ret;
        resp["result"] = true;
        return resp;
    }

    bool check(int row , int col,int userchess,int rowoff,int coloff)
    {
        int count  = 1;
        int newrow = row + rowoff;
        int newcol = col + coloff;
        while(newrow >= 0 && newrow < BOARD_ROW && newcol >= 0 && newcol < BOARD_COL && _board[newrow][newcol] == userchess)
        {   
            ++count;
            if(count == 5)
            {
                return true;
            }
            newrow += rowoff;
            newcol += coloff;
        }
        newrow = row - rowoff;
        newcol = col - coloff;
        while(newrow >= 0 && newrow < BOARD_ROW && newcol >= 0 && newcol < BOARD_COL && _board[newrow][newcol] == userchess)
        {   
            ++count;
            if(count == 5)
            {
                return true;
            }
            newrow -= rowoff;
            newcol -= coloff;
        }
        return false;       
    }

    bool is_win(int row,int col,int userchess)
    {
        return (check(row,col,userchess,0,1) || check(row,col,userchess,-1,0) || check(row,col,userchess,-1,1) || check(row,col,userchess,-1,-1));
    }

    void handle_exit(int uid)
    {
        if(_status == room_status::GAME_START)
        {
            Json::Value resp;
            resp["result"] = true;
            resp["optype"] = "exit";                        
            resp["room_id"] = _roomid;
            resp["uid"] = uid;
            int winner = (uid == _whiteid ? _blackid : _whiteid);
            _table->lose(uid);
            _table->win(winner);
            _status = room_status::GAME_OVER;
            broadcast(resp);
        }
        --_playercount;
    }

    void add_white_user(int uid)
    {
        _whiteid = uid;
        ++_playercount;
    }

    void add_black_user(int uid)
    {
        _blackid = uid;
        ++_playercount;
    }

    int get_rid()
    {
        return _roomid;
    }

    int get_wid()
    {
        return _whiteid;
    }

    int get_bid()
    {
        return _blackid;
    }

    int get_count()
    {
        return _playercount;
    }

    room_status get_status()
    {
        return _status;
    }

private:
    int _roomid;
    room_status _status;
    int _playercount;
    int _whiteid;
    int _blackid;
    user_table* _table;
    online_manager* _online;
    std::vector<std::vector<int>> _board;
};

using room_ptr = std::shared_ptr<room>;

class room_manager
{
public:
    room_manager(user_table* table,online_manager* online)
    :_nextrid(1)
    ,_table(table)
    ,_online(online)
    {
        DLOG("房间管理初始化完毕!");
    }

    ~room_manager()
    {
        DLOG("房间管理销毁!");
    }

    room_ptr create_room(int uid1,int uid2)
    {
        if(_online->is_in_game_hall(uid1) == false || _online->is_in_game_hall(uid2) == false)
        {
            DLOG("用户不在游戏大厅");
            return room_ptr(); 
        }
        std::lock_guard<std::mutex> lg(_mutex);
        room_ptr rp(new room(_nextrid,_table,_online));
        rp->add_white_user(uid1);
        rp->add_black_user(uid2);
        _ridtor.insert(std::make_pair(_nextrid,rp));
        _uidtorid.insert(std::make_pair(uid1,_nextrid));
        _uidtorid.insert(std::make_pair(uid2,_nextrid));
        ++_nextrid;
        return rp;
    }

    room_ptr get_room_by_rid(int rid)
    {   
        std::lock_guard<std::mutex> lg(_mutex);
        auto it = _ridtor.find(rid);
        if(it == _ridtor.end())
        {
            return room_ptr();
        }
        return it->second;
    }

    room_ptr get_room_by_uid(int uid)
    {
        std::unordered_map<int,int>::iterator it;
        {
            std::lock_guard<std::mutex> lg(_mutex);
            it = _uidtorid.find(uid);
            if(it == _uidtorid.end())
            {
                return room_ptr();
            }
        }
        int rid = it ->second;
        return  get_room_by_rid(rid); 
    }

    void remove_room(int rid)
    {
        room_ptr rp = get_room_by_rid(rid);
        if(rp.get() == nullptr)
        {
            return;
        }
        int wid = rp->get_wid();
        int bid = rp->get_bid();
        std::lock_guard<std::mutex> lg(_mutex);
        _uidtorid.erase(wid);
        _uidtorid.erase(bid);
        _ridtor.erase(rid);
    }  
    
    void remove_room_user(int uid)
    {
        room_ptr rp = get_room_by_uid(uid);
        if(rp.get() == nullptr)
        {
            return;
        }
        rp->handle_exit(uid);
        if(rp->get_count() == 0)
        {
            remove_room(rp->get_rid());
        }
        else
        {
            std::lock_guard<std::mutex> lg(_mutex);
            _uidtorid.erase(uid);
        }
    }

private:
    std::mutex _mutex;
    int _nextrid;
    user_table* _table; 
    online_manager* _online; 
    std::unordered_map<int,room_ptr> _ridtor;
    std::unordered_map<int,int> _uidtorid;
};